package com.lzb.common.interceptor;

import java.util.Set;

import javax.annotation.Resource;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import com.lzb.common.helper.redis.RedisHelper;
import com.lzb.common.json.JsonJacksonUtil;

@Component(CacheInterceptor.BEAN_PACKAGE_NAME)
@Aspect
public class CacheInterceptor implements Ordered {

	@Resource(name = RedisHelper.BEAN_PACKAGE_NAME)
	private RedisHelper redisHelper;
	/**
	 * 前置通知
	 */
	private final String BEFORE_POINTCUT_EXECUTION = "execution(* com.lzb..*Service.get*(..)) || "
			+ "execution(* com.lzb..*Service.find*(..)) || "
			+ "execution(* com.lzb..*Service.query*(..)) || "
			+ "execution(* com.lzb..*Service.select*(..))";
	/**
	 * 环绕通知
	 */
	private final String AROUND_POINTCUT_EXECUTION = "execution(* com.lzb..*Service.get*(..)) || "
			+ "execution(* com.lzb..*Service.find*(..)) || "
			+ "execution(* com.lzb..*Service.query*(..)) || "
			+ "execution(* com.lzb..*Service.select*(..))";
	/**
	 * 后置通知
	 */
	private final String AFTER_POINTCUT_EXECUTION = "execution(* com.lzb..*Service.update*(..)) || "
			+ "execution(* com.lzb..*Service.add*(..)) ||"
			+ "execution(* com.lzb..*Service.insert*(..)) || "
			+ "execution(* com.lzb..*Service.del*(..))";
	/**
	 * 后置返回通知
	 */
	private final String AFTER_RETURNING_POINTCUT_EXECUTION = "execution(* com.lzb..*Service.update*(..)) || "
			+ "execution(* com.lzb..*Service.add*(..)) ||"
			+ "execution(* com.lzb..*Service.insert*(..)) || "
			+ "execution(* com.lzb..*Service.del*(..))";
	/*
	 * 执行顺序： 
	 * CacheInterceptor.around() begin 
	 * CacheInterceptor.begin()
	 * MngUserService.getMngUserByUUID() 
	 * begin MngUserService.getMngUserByUUID()
	 * after CacheInterceptor.around() after 
	 * CacheInterceptor.after()
	 * CacheInterceptor.afterReturning()
	 */
	public static final String BEAN_PACKAGE_NAME = "com.lzb.interceptor.CacheInterceptor";

	public CacheInterceptor() {
	}

	/**
	 * 前置通知（Before advice）： 在某连接点（join
	 * point）之前执行的通知，但这个通知不能阻止连接点前的执行（除非它抛出一个异常）。
	 * */
	@Before(BEFORE_POINTCUT_EXECUTION)
	public void before(JoinPoint jp) {}

	/**
	 * 环绕通知（Around Advice）： 包围一个连接点（join point）的通知，如方法调用。这是最强大的一种通知类型。
	 * 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
	 * 
	 * @throws Exception
	 * */
	@Around(AROUND_POINTCUT_EXECUTION)
	public Object around(ProceedingJoinPoint pjp) throws Exception {
		Object result = null;
		StringBuilder key = new StringBuilder(32);
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		// 类名
		String className = signature.getDeclaringTypeName();
		key.append(className).append('.');
		// 方法名
		String methodName = signature.getName();
		key.append(methodName).append('.');
		// 参数数组
		Object[] args = pjp.getArgs();
		if (args != null) {
			StringBuilder sb = new StringBuilder();
			for (Object _arg : args) {
				// Json序列化数组
				sb.append(JsonJacksonUtil.beanToJson(_arg).toString()).append('/');
			}
			key.append(sb);
		}
		try {
			if(redisHelper == null) {
				throw new RuntimeException("RedisHelper为空");
			}
			if (!redisHelper.exists(key.toString())) {
				result = pjp.proceed();
				redisHelper.set(key.toString(), JsonJacksonUtil.beanToJson(result));
			} else {
				String jsonString = redisHelper.get(key.toString());
				result = JsonJacksonUtil.jsonToBean(jsonString, signature.getReturnType());
			}
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 后通知（After (finally) advice）： 当某连接点退出的时候执行的通知（不论是正常返回还是异常退出）。
	 * */
	@After(AFTER_POINTCUT_EXECUTION)
	public void after(JoinPoint pjp) {}

	/**
	 * 返回后通知（After returning advice）： 在某连接点（join
	 * point）正常完成后执行的通知：例如，一个方法没有抛出任何异常，正常返回。
	 * */
	@AfterReturning(AFTER_RETURNING_POINTCUT_EXECUTION)
	public void afterReturning(JoinPoint pjp) {
		MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
		StringBuilder key = new StringBuilder();
		// 类名
		String className = methodSignature.getDeclaringTypeName();
		key.append(className).append(".").append('*');
		Set<String> delKeySet = redisHelper.keys(key.toString());
		//如果缓存没有对象，立即返回，无需删除
		if(null == delKeySet || 0 == delKeySet.size()) return;
		redisHelper.del(delKeySet.toArray(new String[delKeySet.size()]));
	}

	@Override
	public int getOrder() {
		//在Service层的第一个AOP切面
		return 1;
	}

}
